// Copyright (c) XiGuan 2023
// Author： xiguan
// Email: xiguan.teng@qq.com
// Create on 2023/10/17
// TODO:
//

#ifndef WEBSERVER_LOGD_BLOCKDEQUE_H
#define WEBSERVER_LOGD_BLOCKDEQUE_H

#include <cassert>
#include <condition_variable>
#include <deque>
#include <mutex>

template <class T>
class BlockDeque {
 private:
  std::deque<T> deque_;

  size_t capacity_;

  std::mutex mtx_;

  bool is_close_;

  std::condition_variable cond_consumer_;

  std::condition_variable cond_producer_;

 public:
  explicit BlockDeque(size_t _max_capacity = 1000);

  ~BlockDeque();

  void clear();

  void close();

  bool empty();

  bool full();

  size_t size();

  size_t capacity();

  T front();

  T back();

  void push_back(const T &item);

  void push_front(const T &item);

  bool pop(T &item);

  bool pop(T &item, int timeout);

  void flush();
};

template <class T>
BlockDeque<T>::BlockDeque(size_t _max_capacity) {
  assert(_max_capacity > 0);
  is_close_ = false;
}

template <class T>
void BlockDeque<T>::close() {
  {
    // RAII思想 类似智能指针 自动上锁 释放解锁
    std::lock_guard<std::mutex> locker(mtx_);
    // 删除双端队列中的所有元素
    deque_.clear();
    is_close_ = true;
  }

  cond_producer_.notify_all();
  cond_consumer_.notify_all();
}

template <class T>
BlockDeque<T>::~BlockDeque() {
  this->close();
}

template <class T>
void BlockDeque<T>::clear() {
  std::lock_guard<std::mutex> locker(mtx_);
  deque_.clear();
}

template <class T>
bool BlockDeque<T>::empty() {
  std::lock_guard<std::mutex> locker(mtx_);
  return deque_.empty();
}

template <class T>
bool BlockDeque<T>::full() {
  std::lock_guard<std::mutex> locker(mtx_);
  return deque_.size() >= capacity_;
}

template <class T>
size_t BlockDeque<T>::size() {
  std::lock_guard<std::mutex> locker(mtx_);
  return deque_.size();
}

template <class T>
size_t BlockDeque<T>::capacity() {
  std::lock_guard<std::mutex> locker(mtx_);
  return capacity_;
}

template <class T>
T BlockDeque<T>::front() {
  std::lock_guard<std::mutex> locker(mtx_);
  return deque_.front();
}
template <class T>
T BlockDeque<T>::back() {
  std::lock_guard<std::mutex> locker(mtx_);
  return deque_.back();
}

template <class T>
void BlockDeque<T>::flush() {
  cond_consumer_.notify_one();
}

template <class T>
void BlockDeque<T>::push_back(const T &item) {
  std::unique_lock<std::mutex> locker(mtx_);
  while (deque_.size() >= capacity_) {
    cond_producer_.wait(locker);
  }

  deque_.push_back(item);
  cond_consumer_.notify_one();
}

template <class T>
void BlockDeque<T>::push_front(const T &item) {
  std::unique_lock<std::mutex> locker(mtx_);
  while (deque_.size() >= capacity_) {
    cond_producer_.wait(locker);
  }
  deque_.push_front(item);
  cond_consumer_.notify_one();
}

template <class T>
bool BlockDeque<T>::pop(T &item) {
  std::unique_lock<std::mutex> locker(mtx_);
  while (deque_.empty()) {
    cond_consumer_.wait(locker);
    if (is_close_) {
      return false;
    }
  }
  item = deque_.front();
  deque_.pop_front();
  cond_producer_.notify_one();
  return true;
}

template <class T>
bool BlockDeque<T>::pop(T &item, int timeout) {
  std::unique_lock<std::mutex> locker(mtx_);
  while (deque_.empty()) {
    if (cond_consumer_.wait_for(locker, std::chrono::seconds(timeout)) ==
        std::cv_status::timeout) {
      return false;
    }
    if (is_close_) {
      return false;
    }
  }
  item = deque_.front();
  deque_.pop_front();
  cond_producer_.notify_one();
  return true;
}


#endif  // WEBSERVER_LOGD_BLOCKDEQUE_H
